#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <unistd.h>
#include <sys/epoll.h>

#include "Log.hpp"
#include "Inet_Addr.hpp"
#include "Socket.hpp"

using namespace LogModule;
using namespace SocketModule;

class EPollServer
{
    const static int num = 4096; // wait events buffer size
    const static int gdefault = 128;

private:
    void Handler(int n)
    {
        for (int i = 0; i < n; i++)
        {
            int fd = _revs[i].data.fd;
            uint32_t events = _revs[i].events;
            LOG(INFO, "%d ->fd is ready, event is: %s\n", fd, EventsToString(events).c_str());
            if (events & EPOLLIN)
            {
                if (fd == _listensock->Sockfd())
                {
                    Accepter();
                }
                else
                {
                    HandlerIO(fd);
                }
            }
        }
    }

    void Accepter()
    {
        Inet_Addr addrs;
        SockSPtr sock = _listensock->Accepter(&addrs);
        if (sock->Sockfd() < 0)
        {
            LOG(ERROR, "get link error\n");
            return;
        }
        LOG(INFO, "get a new link %d, client info is %s:%d\n",sock->Sockfd(), addrs.Ip().c_str(), addrs.Port());

        struct epoll_event ev;
        ev.events = EPOLLIN;
        ev.data.fd = sock->Sockfd();
        ::epoll_ctl(_epfd, EPOLL_CTL_ADD, sock->Sockfd(), &ev);
        LOG(INFO, "epoll_ctl success! new add socket is:%d\n", sock->Sockfd());
    }

    void HandlerIO(int fd)
    {
        char buffer[num];
        int n = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            // construct a simple http response
            std::string response;
            response = "HTTP/1.0 200 OK\r\n";
            std::string content = "<html><body><h1>hello epoll</h1></body></html>";
            response += "Content-Type: text/html\r\n";
            response += "Content-Length: " + std::to_string(content.size()) + "\r\n\r\n";
            response += content;
            ::send(fd, response.c_str(), response.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit...\n");
            // delete this fd from rb_tree
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            // close fd
            ::close(fd);
        }
        else
        {
            LOG(ERROR, "recv error, close fd %d...\n", fd);
            // delete this fd from rb_tree
            ::epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            // close fd
            ::close(fd);
        }
    }

    std::string EventsToString(uint32_t events)
    {
        std::string eventstr;
        if (events & EPOLLIN)
            eventstr = "EPOLLIN";
        if (events & EPOLLOUT)
            eventstr += "|EPOLLOUT";
        return eventstr;
    }

public:
    EPollServer(uint16_t port)
        : _port(port), _listensock(std::make_unique<TcpSocket>()), _revs(nullptr),_epfd(-1)
    {
        _revs = new struct epoll_event[num];
        _listensock->BuildListenSocket(_port);
        // create epoll moudle
        _epfd = ::epoll_create(gdefault);
        if (_epfd < 0)
        {
            LOG(FATAL, "epoll_create error!\n");
            ::exit(EPOLL_CREATTE_ERROR);
        }
        LOG(INFO, "epoll_create success! epfd:%d\n", _epfd);
    }

    ~EPollServer()
    {
        if (_epfd >= 0)
        {
            ::close(_epfd);
        }
        _listensock->Close();
        // release wait events buffer
        delete[] _revs;
    }

    void InitServer()
    {
        // defaultly, add listen sockfd into epoll moulde
        struct epoll_event ev;
        ev.events = EPOLLIN;                // set the fd expect read event
        ev.data.fd = _listensock->Sockfd(); // For later access
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Sockfd(), &ev);
        if (n < 0)
        {
            LOG(FATAL, "epoll_ctl error!\n");
            ::exit(EPOLL_CTL_ERROR);
        }
        LOG(INFO, "epoll_ctl success! new add socket is:%d\n", _listensock->Sockfd());
    }

    void Loop()
    {
        int timeout = -1;
        while (true)
        {
            int n = ::epoll_wait(_epfd, _revs, num, timeout);
            switch (n)
            {
            case 0:
                LOG(INFO, "time out!\n");
                break;
            case -1:
                LOG(ERROR, "epoll error\n");
                break;
            default:
                LOG(INFO, "have %d event ready\n", n);
                Handler(n);// Notice n is ready fd_Num！！！！
                break;
            }
        }
    }

private:
    uint16_t _port;                      // server port
    std::unique_ptr<Socket> _listensock; // listen sockfd
    struct epoll_event *_revs;           // wait events buffer
    int _epfd;                           // epoll_create return value
};
